#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for fcntl
#include <errno.h> // for errno

#define PORT 8080
#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024

// Function to set socket to non-blocking mode
int set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl F_GETFL");
        return -1;
    }
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
        perror("fcntl F_SETFL O_NONBLOCK");
        return -1;
    }
    return 0;
}

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};
    int client_sockets[MAX_CLIENTS] = {0}; // Simple array to store client sockets
    int client_count = 0;

    // Creating socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // Forcefully attaching socket to the port 8080
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Binding the socket to the network address and port
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // Start listening for connections
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // Set the listening socket to non-blocking
    if (set_nonblocking(server_fd) < 0) {
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port %d (Non-blocking)...\n", PORT);

    while (1) {
        // Try to accept new connections (non-blocking)
        new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
        if (new_socket < 0) {
            if (errno != EWOULDBLOCK && errno != EAGAIN) {
                perror("accept error");
                // Handle other accept errors if necessary
            }
            // No incoming connection, continue loop
        } else {
            printf("New connection accepted, socket fd is %d, ip is : %s, port : %d\n",
                   new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));

            // Set the new client socket to non-blocking
            if (set_nonblocking(new_socket) < 0) {
                close(new_socket);
            } else {
                // Add new client socket to our list (simple version)
                if (client_count < MAX_CLIENTS) {
                    client_sockets[client_count++] = new_socket;
                    printf("Adding client to list. Total clients: %d\n", client_count);
                } else {
                    printf("Max clients reached. Closing new connection.\n");
                    close(new_socket);
                }
            }
        }

        // Check existing clients for data (non-blocking)
        for (int i = 0; i < client_count; i++) {
            int client_fd = client_sockets[i];
            if (client_fd == 0) continue; // Skip empty slots if any

            int valread = recv(client_fd, buffer, BUFFER_SIZE - 1, 0);

            if (valread > 0) {
                // Data received
                buffer[valread] = '\0';
                printf("Received from client %d: %s\n", client_fd, buffer);
                // Echo back (simplified, potentially blocking send ignored for this example focus)
                send(client_fd, buffer, strlen(buffer), 0);
            } else if (valread == 0) {
                // Connection closed by client
                printf("Client %d disconnected\n", client_fd);
                close(client_fd);
                // Remove client from list (simple shift)
                for (int j = i; j < client_count - 1; j++) {
                    client_sockets[j] = client_sockets[j + 1];
                }
                client_sockets[client_count - 1] = 0;
                client_count--;
                i--; // Adjust loop index after removal
            } else { // valread < 0
                if (errno != EWOULDBLOCK && errno != EAGAIN) {
                    // Real error occurred
                    perror("recv error");
                    close(client_fd);
                    // Remove client from list
                    for (int j = i; j < client_count - 1; j++) {
                        client_sockets[j] = client_sockets[j + 1];
                    }
                    client_sockets[client_count - 1] = 0;
                    client_count--;
                    i--; // Adjust loop index after removal
                }
                // EWOULDBLOCK/EAGAIN means no data available right now, continue loop
            }
        }
         // Add a small delay to prevent busy-waiting consuming 100% CPU
         usleep(10000); // Sleep for 10ms
    }

    // Cleanup (server normally runs indefinitely, but good practice)
    close(server_fd);
    return 0;
}